#ifndef THREADPOOL_HPP
#define THREADPOOL_HPP

#include<iostream>
#include<functional>
#include<vector>
#include<mutex>
#include<queue>
#include<condition_variable>
#include<thread>

template<class T>
class BlockQueue{
private:
bool _nonblock;//非阻塞标志
std::mutex _mutex;
std::queue<T> _queue;
std::condition_variable _not_empty;

public:
BlockQueue(bool nonblock=false):_nonblock(nonblock)
{}
//入队
void Push(const T &value)
{
	//加锁(自动)
	std::lock_guard<std::mutex> lock(_mutex);
	//放入任务(简单起见，假设队列不会满)
	_queue.push(value);
	//通知消费者(随即唤醒一个等待的线程)
	_not_empty.notify_one();
}
//出队
bool Pop(T &value)
{
	//加锁（手动）
	std::unique_lock<std::mutex> lock(_mutex);
	//等待
	_not_empty.wait(lock,[this]{return !_queue.empty()||_nonblock;});
	//出队
	value=_queue.front();
	_queue.pop();
	return true;
}
//解除阻塞在当前队列的进程
void Cancel()
{
	//加锁(自动)
	std::lock_guard<std::mutex> lock(_mutex);
	_nonblock=true;
	_not_empty.notify_all();
}
};

template<class T>
class BlockQueuePro
{
	private:
	bool _nonblock;
    std::queue<T> _producer_queue;
    std::queue<T> _consumer_queue;
    std::mutex _producer_mutex;
    std::mutex _consumer_mutex;
    std::condition_variable _not_empty;
	 // 当消费者队列为空时，交换生产者和消费者队列
	 int SwapQueue()
	 {
		std::unique_lock<std::mutex> lock(_producer_mutex);
		_not_empty.wait(lock,[this]{return !_producer_queue.empty()||_nonblock;});
		std::swap(_producer_queue,_consumer_queue);
		return _consumer_queue.size();
	 }
	public:
	BlockQueuePro(bool nonblock = false) : _nonblock(nonblock) {}

    // 入队操作
    void Push(const T &value) {
        std::lock_guard<std::mutex> lock(_producer_mutex);
        _producer_queue.push(value);
        _not_empty.notify_one();
    }
	//出队
	bool Pop(T &value)
	{
		std::unique_lock<std::mutex> lock(_consumer_mutex);
		if(_consumer_queue.empty()&&SwapQueue()==0) return false;
		value=_consumer_queue.front();
		_consumer_queue.pop();
		return true;
	}


	// 解除阻塞在当前队列的线程
    void Cancel() {
        std::lock_guard<std::mutex> lock(_producer_mutex);
        _nonblock = true;
        _not_empty.notify_all();
    }


};


class ThreadPool
{
	private:
	void Worker()
	{
		while(true)
		{
			std::function<void()> task;
			if(!_task_queue.Pop(task)) break;
			task();
		}
	}
	BlockQueuePro<std::function<void()>> _task_queue;
	std::vector<std::thread> _workers; 
	public:
	explicit ThreadPool(int num_threads)
	{
		for(size_t i=0;i<num_threads;i++)
		{
			_workers.emplace_back([this](){Worker();});
		}
	}
	template<typename F,typename... Args>
	void Post(F &&f,Args &&...args)
	{
		auto task=std::bind(std::forward<F>(f),std::forward<Args>(args)...);
		_task_queue.Push(task);
	}
	~ThreadPool()
	{
		_task_queue.Cancel();
		for(auto &worker:_workers)
		{
			if(worker.joinable()) worker.join();
		}
	}

};


#endif